#!/usr/bin/env python3
# -*- encoding: utf-8 -*-


import numpy as np
# from common.functions import *
from common.functions import *


class ReLU:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()      # forward 不改变输入的值
        out[self.mask] = 0

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx


class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx


class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        out = np.dot(self.x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        return dx


class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None
        self.y = None
        self.t = None

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)

        return self.loss

    def backward(self, dout=1.0):
        batch_size = self.t.shape[0]
        if self.y.shape == self.t.shape:
            # 将one-hot-vector类型的监督数据转换为标签
            t = np.argmax(self.t, axis=1)
        else:
            t = self.t
        dx = self.y.copy()
        dx[np.arange(batch_size), t] -= 1
        dx = dx / batch_size

        # batch_size = self.t.shape[0]
        # if self.y.shape == self.t.shape:
        #     # 监督数据是one-hot-vector类型
        #     dx = (self.y - self.t) / batch_size
        #     return dx
        # dx = self.y.copy()      # 不改变本身记录的属性
        # dx[np.arange(batch_size), self.t] -= 1
        # dx = dx / batch_size

        return dx
